
/*
 * TRMutex.cpp
 *
 *  Created on: Nov 23, 2012
 *      Author: dan
 */

#include "TRMutex.h"
#include "Print.hpp"

namespace {

typedef boost::asio::ip::tcp::socket socket_t;
typedef boost::shared_ptr<socket_t> socket_ref;
typedef std::map<std::string, socket_ref> Hosts;
typedef boost::system::error_code error_code;
typedef boost::thread thread_t;
typedef boost::shared_ptr<thread_t> thread_ref;
typedef std::map<std::string, thread_ref> Threads;
typedef boost::asio::io_service ios_t;
typedef boost::shared_ptr<ios_t> ios_ref;

}

TRMutex::TRMutex(Network& network, Network::Team team, std::string m_name) :
		network(network), team(team), name(m_name), time(0), state(MTX_OUT) {
	//debug_print.turn_on();
	boost::mutex::scoped_lock l(mtx);
	callbackId = network.setCallbacks(
			boost::bind(&TRMutex::Event_OnConnection, this, _1, _2),
			boost::bind(&TRMutex::Event_OnDisconnection, this, _1, _2),
			boost::bind(&TRMutex::Event_OnMessage, this, _1, _2, _3));
	init();
	for(size_t i=0;i<team.size();i++){
		PRINT("check "<<team[i].str());
		if(network.isMe(team[i].str())){
			PRINT("It is ME i="<<i);
			next = team[(i+1)%team.size()].str();
			IAmFirst = i==0;
			if(IAmFirst) PRINT("I AM FIRST");
			break;
		}
	}
}
TRMutex::~TRMutex() {
	network.removeCallbacks(callbackId);
}

void TRMutex::Event_OnConnection(std::string id, error_code error) {
	PRINT("TRMutex("+name+"): EVENT<CONNECTION>: id="<<id<<", error="<<error);
	if(error)return;
	boost::mutex::scoped_lock l(mtx);

	if (inTeam(id) == false)
		return;
	connected[id] = true;

	if(state==MTX_WAIT_CONNECTION){
		network.send(next, getMutexMessage(), error);
		state = MTX_OUT;
	}

	PRINT("notify about changes.");
	state_changed.notify_one();
}
void TRMutex::Event_OnDisconnection(std::string id, error_code error) {
	PRINT("TRMutex("+name+"): EVENT<DISCONNECTION>: id="<<id<<", error="<<error);
	//if(error) return;
	boost::mutex::scoped_lock l(mtx);

	if (inTeam(id) == false)
		return;
	connected[id] = false;
	PRINT("notify about changes.");
	state_changed.notify_one();
}
void TRMutex::Event_OnMessage(std::string id, std::string message, error_code error) {
	PRINT("TRMutex("+name+"): EVENT<MESSAGE>: id="<<id<<", error="<<error<<", message="<<message);
	if(error) return;

	boost::mutex::scoped_lock l(mtx);

	if (inTeam(id) == false || isMutexMessage(message)==false){
		PRINT("this message not from my team or it's not a mutex message");
		return;
	}

	error_code send_error;
	if(state==MTX_IN){
		PRINT("I am in section. SOMETHING WRONG");
	}else if(state==MTX_OUT){
		PRINT("I am not in section. send to next ["<<next<<"]");
		if(allConnected()){
			network.send(next, getMutexMessage(), send_error);
			if(send_error){
				PRINT("SEND ERROR: "<<send_error);
			}
		}else{
			state=MTX_WAIT_CONNECTION;
		}
	}else if(state==MTX_WAIT){
		state=MTX_IN;
	}

	PRINT("notify about changes.");
	state_changed.notify_one();

}

void TRMutex::waitConnection() {
	boost::mutex::scoped_lock l(mtx);
	if(IAmFirst) state=MTX_WAIT_CONNECTION;
	while (true) {
		if (allConnected() == true){

			return;
		}
		state_changed.wait(l);
	}
}
void TRMutex::waitConnection(boost::mutex::scoped_lock& ext_l) {
	boost::mutex::scoped_lock l(mtx);
	bool ext_l_locked = true;
	while (true) {
		if (allConnected() == true) {
			if (ext_l_locked == false)
				ext_l.lock();
			return;
		}
		ext_l_locked = false;
		ext_l.unlock();
		state_changed.wait(l);
	}
}

bool TRMutex::lock() {
	boost::mutex::scoped_lock l(mtx);
	state=MTX_WAIT;
	while (true) {
		if (allConnected() == false) {
			PRINT("TRMutex("+name+"): allConnected = false");
			init();
			state = MTX_OUT;
			return false;
		}
		if (state==MTX_IN) {
			PRINT("TRMutex("+name+"): status changed");
			init();
			return true;
		}
		PRINT("TRMutex("+name+"): wait for changes.");
		state_changed.wait(l);
		PRINT("TRMutex("+name+"): something changed in mutex status");
	}
	init();
	state = MTX_OUT;
	return false;
}


void TRMutex::unlock(){
	boost::mutex::scoped_lock l(mtx);
	error_code error;
	network.send(next, getMutexMessage(), error);
	state = MTX_OUT;
}

bool TRMutex::allConnected() {
	for (HostList::iterator i = connected.begin(); i != connected.end(); i++)
		if (i->second == false)
			return false;
	return true;
}

void TRMutex::init() {
	teamSet.clear();
	std::set<std::string> active = network.getConnectionsSet();
	for (size_t i = 0; i < team.size(); i++) {
		std::string member = team[i].str();
		connected[member] = active.find(member) != active.end()
				|| network.isMe(member);
		teamSet.insert(member);
	}
	printTeamStatus();
}
void TRMutex::printTeamStatus() {
	for (size_t i = 0; i < team.size(); i++) {
		std::string member = team[i].str();
		PRINT("TRMutex("+name+"): team member ["+member+"] is " <<(connected[member]?"connected":"disconnected"));
	}
}
bool TRMutex::inTeam(std::string id) {
	return teamSet.find(id) != teamSet.end();
}

std::string TRMutex::getMutexMessage()const{
	std::stringstream s; s<<"@trmutex:"<<name;
	return s.str();
}
bool TRMutex::isMutexMessage(const std::string& message)const{
	return message == getMutexMessage();
}











